/*
 * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.inspur.edp.web.npmpackage.core.npminstall.global;

import com.inspur.edp.web.common.entity.CommandExecutedResult;
import com.inspur.edp.web.common.environment.ExecuteEnvironment;
import com.inspur.edp.web.common.environment.checker.ExecuteEnvironmentCheckResult;
import com.inspur.edp.web.common.environment.checker.ExecuteEnvironmentChecker;
import com.inspur.edp.web.common.io.FileUtility;
import com.inspur.edp.web.common.logger.WebLogger;
import com.inspur.edp.web.common.utility.CommandLineUtility;
import com.inspur.edp.web.common.utility.CommonUtility;
import com.inspur.edp.web.common.utility.ResourceLocalizeUtil;
import com.inspur.edp.web.common.utility.StringUtility;
import com.inspur.edp.web.npmpackage.api.constant.I18nMsgConstant;
import com.inspur.edp.web.npmpackage.api.entity.NpmInstallParameter;
import com.inspur.edp.web.npmpackage.api.entity.NpmPackageResponse;
import com.inspur.edp.web.npmpackage.api.entity.packagejson.NpmPackageJsonDependencyInfo;
import com.inspur.edp.web.npmpackage.api.entity.packagejson.NpmPackageJsonInfo;
import com.inspur.edp.web.npmpackage.api.entity.settings.NpmSettings;
import com.inspur.edp.web.npmpackage.api.entity.settings.NpmUpdatePolicy;
import com.inspur.edp.web.npmpackage.core.npminstall.NpmInstallLockFilePathGenerator;
import com.inspur.edp.web.npmpackage.core.npmlogin.NpmLoginCommandExecutor;
import com.inspur.edp.web.npmpackage.core.npmpackagecheck.NpmPackageCheck;
import com.inspur.edp.web.npmpackage.core.npmsetting.NpmSettingConvertor;
import com.inspur.edp.web.npmpackage.core.npmsetting.NpmSettingManager;

import java.io.File;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;

/**
 * npm全局安装
 *
 * @author noah
 */
public class NpmInstallGlobalManager {
    private static final Object _lockObj = new Object();

    /**
     * 执行默认的安装操作
     *
     * @param isUpgradeTool
     * @return
     */
    public static NpmPackageResponse npmInstallWithDefault(boolean isUpgradeTool) {
        NpmInstallParameter packageParameter = getCurrentNpmInstallParameter(isUpgradeTool);
        // 依据参数进行默认目录的安装

        return npmInstall(packageParameter);
    }

    /**
     * 获取当前的npm参数配置
     * @param isUpgradeTool
     * @return
     */
    public static NpmInstallParameter getCurrentNpmInstallParameter(boolean isUpgradeTool) {
        NpmSettings npmSettings = NpmSettingManager.getNpmSetting(isUpgradeTool);
        NpmInstallParameter packageParameter = NpmSettingConvertor.convertFromNpmSetting(npmSettings);
        return packageParameter;
    }

    /**
     * 全局离线包执行安装
     *
     * @param npmInstallParameter
     * @return
     */
    public static NpmPackageResponse npmInstall(NpmInstallParameter npmInstallParameter) {
        String currentClassName = NpmInstallGlobalManager.class.getName();

        // 执行global 是否需要安装检测
        NpmInstallGlobalChecker.NpmInstallGlobalCheckResult checkResult = NpmInstallGlobalChecker.check(npmInstallParameter);
        if (!checkResult.isNeedInstall()) {
            WebLogger.Instance.info(checkResult.getReason(), currentClassName);
            return NpmPackageResponse.create();
        }

        boolean isUpgradeTool = npmInstallParameter.getExecuteEnvironment().equals(ExecuteEnvironment.UpgradeTool);
        String tmpPackageJsonPath = GlobalPackageJsonPathGenerator.getPackageJsonPathInTmpDir();
        String serverPackageJsonPath = GlobalPackageJsonPathGenerator.generate();
        // 获取package.json 文件路径
        String tmpPackageJsonFilePath = GlobalPackageJsonPathGenerator.getPackageJsonFilePathInTmpDir();

        String lockFilePath = NpmInstallLockFilePathGenerator.generate(tmpPackageJsonPath);


        // 根据server启动时间和当前创建时间来进行比较  如果比server创建时间新 那么自动删除
        long serverStartTime = CommonUtility.getServerStartTime();
        File lockFileInfo = new File(lockFilePath);
        boolean needAutoDeleteLockFile = lockFileInfo.exists() && lockFileInfo.lastModified() <= serverStartTime;
        boolean reWithUnFinishInstall = false;
        if (needAutoDeleteLockFile) {
            FileUtility.deleteFile(lockFilePath);
            reWithUnFinishInstall = true;
        }

        synchronized (_lockObj) {
            if (FileUtility.exists(lockFilePath)) {
                WebLogger.Instance.info(ResourceLocalizeUtil.getString(I18nMsgConstant.WEB_NPM_PACKAGE_MSG_0007), currentClassName);
                return NpmPackageResponse.createError(ResourceLocalizeUtil.getString(I18nMsgConstant.WEB_NPM_PACKAGE_MSG_0007));
            }
        }

        boolean needInstall = false;
        boolean isSameCopiledFile = false;
        boolean installJit = false;

        // 如果文件目录不存在 那么创建对应的文件目录 拷贝package.json 到目标文件目录
        if (!FileUtility.exists(tmpPackageJsonPath)) {
            FileUtility.createDirectory(tmpPackageJsonPath);
        }

        if (!FileUtility.exists(serverPackageJsonPath)) {
            // 如果server中package.json 文件不存在 那么无需执行
            return NpmPackageResponse.create();
        }

        if (!FileUtility.exists(tmpPackageJsonFilePath) || reWithUnFinishInstall) {
            // 如果目标文件不存在 那么直接进行拷贝 并且进行安装
            needInstall = true;
            isSameCopiledFile = true;
        }


        // 如果不是直接拷贝 那么比较两个文件目录是否相同
        // 比较package.json
        String serverPackageJsonContent = FileUtility.readAsString(serverPackageJsonPath);
        NpmPackageJsonInfo serverPackageJsonInfo = NpmPackageCheck.packageJsonInfoGenerate(serverPackageJsonContent);

        NpmPackageJsonInfo tmpPackageJsonInfo = null;
        boolean mustInstallGlobalCommand = checkNeedInstallGlobalCommand();
        String jitVersion;
        // 如果是命令未安装 或者是对应的版本信息不匹配
        if (mustInstallGlobalCommand) {
            // 使用配置的参数进行强制安装
            needInstall = true;
            isSameCopiledFile = true;
        } else {
            if (!isSameCopiledFile) {
                String tmpPackageJsonContent = FileUtility.readAsString(tmpPackageJsonFilePath);
                tmpPackageJsonInfo = NpmPackageCheck.packageJsonInfoGenerate(tmpPackageJsonContent);
            }

            if (!needInstall) {
                if (serverPackageJsonInfo.equals(tmpPackageJsonInfo)) {
                    CommandExecutedResult globalJit = CommandLineUtility.runCommandWithoutThrows("jit --version");
                    if (globalJit.getExitCode() == 0) {
                        List<NpmPackageJsonDependencyInfo> dependencies = serverPackageJsonInfo.getDependencies();
                        NpmPackageJsonDependencyInfo farrisJieEngine = (NpmPackageJsonDependencyInfo)dependencies.stream().filter((dependency) -> {
                            return dependency.getKey().equals("@farris/jit-engine");
                        }).findFirst().orElse(null);
                        if (farrisJieEngine != null) {
                            jitVersion = globalJit.getOutputInfo();
                            if (!jitVersion.equals(farrisJieEngine.getValue())) {
                                needInstall = true;
                                installJit = true;
                            }
                        }
                    }
                } else {
                    needInstall = true;
                }
            }
        }


        // 逐个执行安装
        try {
            if (needInstall && serverPackageJsonInfo != null) {
                synchronized (_lockObj) {
                    FileUtility.createFile(lockFilePath);
                }
                WebLogger.Instance.info(ResourceLocalizeUtil.getString(I18nMsgConstant.WEB_NPM_PACKAGE_MSG_0008), currentClassName);
                WebLogger.Instance.info(ResourceLocalizeUtil.getString(I18nMsgConstant.WEB_NPM_PACKAGE_MSG_0009, tmpPackageJsonFilePath), currentClassName);

                NpmPackageJsonInfo finalNodeModulesPackageJsonInfo = tmpPackageJsonInfo;
                boolean finalIsSameCopiledFile = isSameCopiledFile;

                boolean needLogin = !StringUtility.isNullOrEmpty(npmInstallParameter.getUserName()) && !StringUtility.isNullOrEmpty(npmInstallParameter.getPassword());
                if (needLogin) {
                    String currentWorkPath = FileUtility.getCurrentWorkPath(isUpgradeTool);
                    NpmLoginCommandExecutor.execute(npmInstallParameter, currentWorkPath);
                }

                AtomicReference<NpmPackageResponse> commandResponse = new AtomicReference<>();
                final boolean[] canContinue = {true};
                boolean finalInstallJit = installJit;
                serverPackageJsonInfo.getDependencies().forEach(t -> {
                    if (canContinue[0]) {
                        // 如果是复制的文件 那么直接进行安装
                        if (finalIsSameCopiledFile) {
                            commandResponse.set(GlobalNpmInstallCommandExecutor.execute(npmInstallParameter, t.getKey(), t.getValue()));
                        } else {
                            if (!finalNodeModulesPackageJsonInfo.equalsInDependencies(t.getKey(), t.getValue())) {
                                commandResponse.set(GlobalNpmInstallCommandExecutor.execute(npmInstallParameter, t.getKey(), t.getValue()));
                            }
                            if (finalInstallJit && "@farris/jit-engine".equals(t.getKey())) {
                                commandResponse.set(GlobalNpmInstallCommandExecutor.execute(npmInstallParameter, t.getKey(), t.getValue()));
                            }
                        }
                        if (commandResponse.get() != null && !commandResponse.get().isSuccess()) {
                            canContinue[0] = false;
                        }
                    }
                });
                if (commandResponse.get() != null && !commandResponse.get().isSuccess()) {
                    String errorMessage = ResourceLocalizeUtil.getString(I18nMsgConstant.WEB_NPM_PACKAGE_MSG_0010, commandResponse.get().getErrorMessage());
                    WebLogger.Instance.info(errorMessage, currentClassName);
                    // 如果安装失败 下次要重新安装
                    FileUtility.deleteFile(tmpPackageJsonFilePath);
                    return NpmPackageResponse.createError(errorMessage);
                }
                WebLogger.Instance.info(ResourceLocalizeUtil.getString(I18nMsgConstant.WEB_NPM_PACKAGE_MSG_0011), currentClassName);
                FileUtility.copyFile(serverPackageJsonPath, tmpPackageJsonFilePath, true);
            } else {
                WebLogger.Instance.info(ResourceLocalizeUtil.getString(I18nMsgConstant.WEB_NPM_PACKAGE_MSG_0012), currentClassName);
            }
        } finally {
            if (FileUtility.exists(lockFilePath)) {
                FileUtility.deleteFile(lockFilePath);
            }
        }
        return NpmPackageResponse.create();
    }

    /**
     * 如果检测到未安装jit或ng命令 那么需要执行对应的在线安装
     *
     * @return
     */
    private static boolean checkNeedInstallGlobalCommand() {
        // 检测全局命令jit和ng是否正确安装 如果未正确安装 那么在开启在线更新模式下自动安装
        ExecuteEnvironmentCheckResult jitCheckResult = ExecuteEnvironmentChecker.checkGlobalJitEngineInstalled();
        ExecuteEnvironmentCheckResult ngCheckResult = ExecuteEnvironmentChecker.checkGlobalNgInstalled();
        return !jitCheckResult.isSuccess() || !ngCheckResult.isSuccess();
    }
}
